package org.ehe.business.drawing.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.checkerframework.checker.units.qual.A;
import org.ehe.business.drawing.domain.DesignDrawing;
import org.ehe.business.drawing.domain.DesignDrawingFiles;
import org.ehe.business.drawing.domain.DesignDrawingSite;
import org.ehe.business.drawing.domain.bo.DesignDrawingBo;
import org.ehe.business.drawing.domain.dto.BatchUploadResultDTO;
import org.ehe.business.drawing.domain.dto.DesignDrawingDTO;
import org.ehe.business.drawing.domain.vo.DesignDrawingFilesVO;
import org.ehe.business.drawing.domain.vo.DesignDrawingVO;
import org.ehe.business.drawing.mapper.DesignDrawingMapper;
import org.ehe.business.drawing.service.DesignDrawingFilesService;
import org.ehe.business.drawing.service.DesignDrawingSiteService;
import org.ehe.business.drawing.service.DesignDrawingService;
import org.ehe.business.engineer.service.EngineerService;
import org.ehe.business.project.domain.ProjectDrawRelation;
import org.ehe.business.project.domain.ProjectEntity;
import org.ehe.business.project.domain.vo.ProjectVo;
import org.ehe.business.project.service.ProjectDrawRelationService;
import org.ehe.business.project.service.ProjectService;
import org.ehe.common.core.constant.RoleKeyConstants;
import org.ehe.common.mybatis.core.page.PageQuery;
import org.ehe.common.mybatis.core.page.TableDataInfo;
import org.ehe.common.satoken.utils.LoginHelper;
import org.ehe.system.domain.SysOss;
import org.ehe.system.domain.vo.SysOssVo;
import org.ehe.system.domain.vo.SysUserVo;
import org.ehe.system.mapper.SysOssMapper;
import org.ehe.system.service.ISysOssService;
import org.ehe.system.service.ISysUserService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 设计图纸实现类
 *
 * @author :zhangnn
 * @className :DesignDrawingServiceImpl
 * @description: TODO
 * @date 2025-07-22 10:26:07
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DesignDrawingServiceImpl extends ServiceImpl<DesignDrawingMapper, DesignDrawing>
        implements DesignDrawingService {

    // 任务结果缓存（实际项目应使用Redis）
    private static final Map<String, BatchUploadResultDTO> TASK_CACHE = new ConcurrentHashMap<>();

    @Autowired
    private ISysOssService ossService;
    @Autowired
    private SysOssMapper ossMapper;
    @Autowired
    private ISysUserService userService;
    @Autowired
    private DesignDrawingFilesService designDrawingFilesService;
    @Autowired
    private DesignDrawingSiteService designDrawingSiteService;

    /**
     * 分页查询图纸列表
     * 权限控制：
     * - admin角色可以看到所有图纸
     * - 普通工程师只能看到自己创建的图纸
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 分页结果
     */
    @Override
    public TableDataInfo<DesignDrawingVO> pageDrawings(DesignDrawingBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<DesignDrawing> lqw = buildQueryWrapper(bo);

        // 如果不是管理员角色，则只能看到自己创建的图纸
        if (!(StpUtil.hasRoleOr(RoleKeyConstants.getAdminRoles().toArray(new String[0])))) {
            // 获取当前登录用户名
            String currentUsername = LoginHelper.getUsername();
            // 添加创建者过滤条件
            lqw.eq(DesignDrawing::getCreateBy, currentUsername);
        }

        Page<DesignDrawingVO> page = baseMapper.selectVoPage(pageQuery.build(), lqw);
        page.getRecords().forEach(p -> {
            List<DesignDrawingFiles> list = designDrawingFilesService
                    .list(new LambdaQueryWrapper<DesignDrawingFiles>().eq(DesignDrawingFiles::getDrawId, p.getId()));

            list.forEach(f -> {
                Long attachmentId = f.getAttachmentId();
                SysOssVo ossVo = ossService.getById(attachmentId);
                if (Objects.nonNull(ossVo)) {
                    p.setDrawUrl(ossVo.getUrl());
                    p.setDrawName(ossVo.getOriginalName());
                    p.setDrawType(ossVo.getFileSuffix());
                }
            });
            SysUserVo sysUserVo = userService.selectUserByUserName(p.getCreateBy());
            if (Objects.nonNull(sysUserVo)) {
                p.setDesigner(sysUserVo.getNickName());
            }
        });

        return TableDataInfo.build(page);
    }

    private LambdaQueryWrapper<DesignDrawing> buildQueryWrapper(DesignDrawingBo bo) {
        LambdaQueryWrapper<DesignDrawing> lqw = Wrappers.lambdaQuery();
        lqw.eq(null != bo.getProjectId(), DesignDrawing::getProjectId, bo.getProjectId());
        if ((StringUtils.isNotBlank(bo.getDrawName()) ||
                StringUtils.isNotBlank(bo.getDrawCode()) ||
                StringUtils.isNotBlank(bo.getSecretLevel()))
                && 0 == bo.getSiteId()) {
            bo.setSiteId(null);
        }
        lqw.eq(null != bo.getSiteId(), DesignDrawing::getSiteId, bo.getSiteId());
        lqw.like(StringUtils.isNotBlank(bo.getDrawName()), DesignDrawing::getDrawName, bo.getDrawName());
        lqw.like(StringUtils.isNotBlank(bo.getDrawCode()), DesignDrawing::getDrawCode, bo.getDrawCode());
        lqw.eq(StringUtils.isNotBlank(bo.getSecretLevel()), DesignDrawing::getSecretLevel, bo.getSecretLevel());
        lqw.orderByDesc(DesignDrawing::getCreateTime);
        return lqw;
    }

    /**
     * 新增
     *
     * @param dtos
     * @return
     */
    @Override
    public Long addDrawing(List<DesignDrawingDTO> dtos) {
        // 检查图纸编码是否已存在
        if (checkDrawCodeExists(dtos.get(0).getDrawCode(), null)) {
            throw new RuntimeException("图纸编码已存在");
        }
        if (checkDrawNoExists(dtos.get(0).getDrawNo(), null)) {
            throw new RuntimeException("图号已存在");
        }

        for (DesignDrawingDTO dto : dtos) {
            DesignDrawing drawing = new DesignDrawing();
            BeanUtils.copyProperties(dtos.get(0), drawing);
            if (StringUtils.isBlank(drawing.getVersion())) {
                drawing.setVersion("1.0.0");
            }
            drawing.setDesigner(LoginHelper.getUsername());
            drawing.setCreateBy(LoginHelper.getUsername());
            drawing.setTenantId(LoginHelper.getTenantId());
            save(drawing);

            Long id = drawing.getId();
            DesignDrawingFiles drawingFiles = new DesignDrawingFiles();
            BeanUtils.copyProperties(dto, drawingFiles);
            drawingFiles.setDrawId(id);
            if (StringUtils.isBlank(drawing.getVersion())) {
                drawingFiles.setVersion("1.0.0");
            }
            drawingFiles.setCreateBy(LoginHelper.getUsername());
            drawingFiles.setTenantId(LoginHelper.getTenantId());
            designDrawingFilesService.save(drawingFiles);
        }

        return 0L;
    }

    /**
     * 修改
     *
     * @param dtos
     * @return
     */
    @Override
    public Boolean editDrawing(List<DesignDrawingDTO> dtos) {
        DesignDrawingDTO dto = dtos.get(0);
        // 检查图纸编码是否已存在
        if (checkDrawCodeExists(dto.getDrawCode(), dto.getId())) {
            throw new RuntimeException("图纸编码已存在");
        }
        if (checkDrawNoExists(dto.getDrawNo(), dto.getId())) {
            throw new RuntimeException("图号已存在");
        }

        // 修改基本信息
        DesignDrawing drawing = new DesignDrawing();
        BeanUtils.copyProperties(dto, drawing);
        drawing.setUpdateBy(LoginHelper.getUsername());
        drawing.setTenantId(LoginHelper.getTenantId());

        boolean b = updateById(drawing);
        // 修改文件信息
        if (b) {
            DesignDrawingFiles drawingFiles = this.designDrawingFilesService
                    .getOne(new LambdaQueryWrapper<DesignDrawingFiles>()
                            .eq(DesignDrawingFiles::getDrawId, drawing.getId()));
            drawingFiles.setDrawId(dto.getId());
            drawingFiles.setAttachmentId(dto.getAttachmentId());
            if (StringUtils.isBlank(drawing.getVersion())) {
                drawingFiles.setVersion("1.0.0");
            }
            drawingFiles.setCreateBy(LoginHelper.getUsername());
            drawingFiles.setTenantId(LoginHelper.getTenantId());
            designDrawingFilesService.updateById(drawingFiles);
        }
        return true;
    }

    /**
     * 删除
     *
     * @param id
     * @return
     */
    @Override
    public Boolean deleteDrawing(Long id) {
        return removeById(id);
    }

    /**
     * 批量删除
     *
     * @param ids
     * @return
     */
    @Override
    public Boolean batchDeleteDrawings(Long[] ids) {
        // 先删除文件
        for (Long id : ids) {

        }

        return removeByIds(Arrays.asList(ids));
    }

    /**
     * 获取详情
     *
     * @param id
     * @return
     */
    @Override
    public DesignDrawingVO getDrawingDetail(Long id) {
        DesignDrawingVO vo = baseMapper.selectVoById(id);
        // 获取文件信息
        List<SysOssVo> sysOssVos = new ArrayList<>();
        List<DesignDrawingFiles> designDrawingFiles = designDrawingFilesService.list(
                new LambdaQueryWrapper<DesignDrawingFiles>().eq(DesignDrawingFiles::getDrawId, id));
        // 获取所有附件信息
        designDrawingFiles.forEach(file -> {
            List<SysOssVo> ossVoList = new ArrayList<>();
            // 若存在附件ID，查询并添加到列表
            if (file.getAttachmentId() != null) {
                SysOssVo ossVo = ossService.getById(file.getAttachmentId());
                if (Objects.nonNull(ossVo)) {
                    ossVoList.add(ossVo);
                }
            }
            // 直接为当前file设置ossVoList
            file.setOssVoList(ossVoList);
        });
        // 将处理后的文件列表设置到VO中
        vo.setFiles(designDrawingFiles);
        return vo;
    }

    /**
     * 提取BOM
     *
     * @param id
     * @return
     */
    @Override
    public Boolean extractBom(Long id) {
        // 这里实现BOM提取逻辑
        // 可以调用外部服务或者更新状态等
        DesignDrawing drawing = getById(id);
        if (drawing == null) {
            throw new RuntimeException("图纸不存在");
        }

        // 检查文件格式是否支持BOM提取
        if (!"dwg".equalsIgnoreCase(drawing.getDrawType()) &&
                !"pdf".equalsIgnoreCase(drawing.getDrawType()) &&
                !"dxf".equalsIgnoreCase(drawing.getDrawType())) {
            throw new RuntimeException("当前文件格式不支持BOM提取");
        }

        // 模拟BOM提取过程
        // 实际项目中这里应该调用BOM提取服务

        return true;
    }

    /**
     * 检查图纸编码是否已经存在
     *
     * @param drawCode
     * @param excludeId
     * @return
     */
    @Override
    public Boolean checkDrawCodeExists(String drawCode, Long excludeId) {
        LambdaQueryWrapper<DesignDrawing> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DesignDrawing::getDrawCode, drawCode);
        if (excludeId != null) {
            wrapper.ne(DesignDrawing::getId, excludeId);
        }
        wrapper.groupBy(DesignDrawing::getDrawCode);
        return count(wrapper) > 1;
    }

    /**
     * 检查号是否已经存在
     *
     * @param drawNo
     * @param excludeId
     * @return
     */
    public Boolean checkDrawNoExists(String drawNo, Long excludeId) {
        LambdaQueryWrapper<DesignDrawing> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DesignDrawing::getDrawCode, drawNo);
        if (excludeId != null) {
            wrapper.ne(DesignDrawing::getId, excludeId);
        }
        return count(wrapper) > 0;
    }

    /**
     * 生成文件编码
     *
     * @return
     */
    @Override
    public String buildDrawCode() {
        String businessPrefix = "EIAHE";
        LocalDate today = LocalDate.now();
        String dateStr = today.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        // 构造当天起始时间（00:00:00）和次日起始时间（00:00:00）
        LocalDateTime startOfDay = today.atStartOfDay();
        LocalDateTime endOfDay = today.plusDays(1).atStartOfDay();

        // 查询当天创建的记录数量
        Long todayCount = this.baseMapper.selectCount(
                new QueryWrapper<DesignDrawing>()
                        .ge("create_time", startOfDay) // 大于等于当天0点
                        .lt("create_time", endOfDay) // 小于次日0点
        );
        // 序列号 = 今日数量 + 1，格式化补0
        String serialNumber = String.format("%03d", todayCount + 1);

        // 拼接编码
        return String.join("_", businessPrefix, dateStr, serialNumber);
    }

    /**
     * 获取图纸统计信息
     *
     * @return 统计数据
     */
    @Override
    public Map<String, Object> getDrawingStatistics() {
        Map<String, Object> statistics = new HashMap<>();

        // 总数统计
        long totalCount = count();
        statistics.put("totalCount", totalCount);

        // 按类型统计
        QueryWrapper<DesignDrawing> typeQuery = new QueryWrapper<>();
        typeQuery.select("draw_type", "COUNT(*) as count")
                .groupBy("draw_type");
        List<Map<String, Object>> typeStats = baseMapper.selectMaps(typeQuery);
        statistics.put("typeStats", typeStats);

        // 按密级统计
        QueryWrapper<DesignDrawing> secretQuery = new QueryWrapper<>();
        secretQuery.select("secret_level", "COUNT(*) as count")
                .groupBy("secret_level");
        List<Map<String, Object>> secretStats = baseMapper.selectMaps(secretQuery);
        statistics.put("secretStats", secretStats);

        // 按创建时间统计（最近7天）
        LocalDateTime sevenDaysAgo = LocalDateTime.now().minusDays(7);
        long recentCount = count(new QueryWrapper<DesignDrawing>()
                .ge("create_time", sevenDaysAgo));
        statistics.put("recentCount", recentCount);

        // 按创建者统计（Top 10）
        QueryWrapper<DesignDrawing> creatorQuery = new QueryWrapper<>();
        creatorQuery.select("create_by", "COUNT(*) as count")
                .groupBy("create_by")
                .orderByDesc("COUNT(*)")
                .last("LIMIT 10");
        List<Map<String, Object>> creatorStats = baseMapper.selectMaps(creatorQuery);
        statistics.put("creatorStats", creatorStats);

        return statistics;
    }

    /**
     * 获取搜索建议
     *
     * @param keyword 关键词
     * @return 搜索建议
     */
    @Override
    public Map<String, Object> getSearchSuggestions(String keyword) {
        Map<String, Object> suggestions = new HashMap<>();

        if (StringUtils.isBlank(keyword) || keyword.length() < 2) {
            return suggestions;
        }

        // 图纸名称建议
        LambdaQueryWrapper<DesignDrawing> nameQuery = Wrappers.lambdaQuery();
        nameQuery.select(DesignDrawing::getDrawName)
                .like(DesignDrawing::getDrawName, keyword)
                .groupBy(DesignDrawing::getDrawName)
                .orderByDesc(DesignDrawing::getCreateTime)
                .last("LIMIT 10");
        List<DesignDrawing> nameList = list(nameQuery);
        suggestions.put("names", nameList.stream()
                .map(DesignDrawing::getDrawName)
                .collect(Collectors.toList()));

        // 图纸编码建议
        LambdaQueryWrapper<DesignDrawing> codeQuery = Wrappers.lambdaQuery();
        codeQuery.select(DesignDrawing::getDrawCode)
                .like(DesignDrawing::getDrawCode, keyword)
                .groupBy(DesignDrawing::getDrawCode)
                .orderByDesc(DesignDrawing::getCreateTime)
                .last("LIMIT 10");
        List<DesignDrawing> codeList = list(codeQuery);
        suggestions.put("codes", codeList.stream()
                .map(DesignDrawing::getDrawCode)
                .collect(Collectors.toList()));

        // 创建者建议
        LambdaQueryWrapper<DesignDrawing> creatorQuery = Wrappers.lambdaQuery();
        creatorQuery.select(DesignDrawing::getCreateBy)
                .like(DesignDrawing::getCreateBy, keyword)
                .groupBy(DesignDrawing::getCreateBy)
                .orderByDesc(DesignDrawing::getCreateTime)
                .last("LIMIT 5");
        List<DesignDrawing> creatorList = list(creatorQuery);
        suggestions.put("creators", creatorList.stream()
                .map(DesignDrawing::getCreateBy)
                .collect(Collectors.toList()));

        return suggestions;
    }

    /**
     * 导出图纸清单
     *
     * @param bo 查询条件
     * @return 导出文件路径
     */
    @Override
    public String exportDrawingList(DesignDrawingBo bo) {
        // 查询数据
        List<DesignDrawingVO> dataList = baseMapper.selectVoList(buildQueryWrapper(bo));

        // 创建工作簿
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("图纸清单");

        // 设置标题样式
        CellStyle titleStyle = workbook.createCellStyle();
        Font titleFont = workbook.createFont();
        titleFont.setBold(true);
        titleFont.setFontHeightInPoints((short) 12);
        titleStyle.setFont(titleFont);
        titleStyle.setAlignment(HorizontalAlignment.CENTER);

        // 创建标题行
        Row titleRow = sheet.createRow(0);
        String[] titles = {
                "序号", "图纸名称", "图纸编码", "图纸类型", "密级", "版本号",
                "文件大小", "创建者", "创建时间", "描述"
        };

        for (int i = 0; i < titles.length; i++) {
            Cell cell = titleRow.createCell(i);
            cell.setCellValue(titles[i]);
            cell.setCellStyle(titleStyle);
        }

        // 填充数据
        for (int i = 0; i < dataList.size(); i++) {
            Row row = sheet.createRow(i + 1);
            DesignDrawingVO drawing = dataList.get(i);

            row.createCell(0).setCellValue(i + 1);
            row.createCell(1).setCellValue(drawing.getDrawName());
            row.createCell(2).setCellValue(drawing.getDrawCode());
            row.createCell(3).setCellValue(drawing.getDrawType());
            row.createCell(4).setCellValue(drawing.getSecretLevel());
            row.createCell(5).setCellValue(drawing.getVersion());

            row.createCell(7).setCellValue(drawing.getCreateBy());
            row.createCell(8).setCellValue(
                    drawing.getCreateTime() != null
                            ? new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(drawing.getCreateTime())
                            : "-");
            row.createCell(9).setCellValue(drawing.getRemark());
        }

        // 自动调整列宽
        for (int i = 0; i < titles.length; i++) {
            sheet.autoSizeColumn(i);
        }

        // 保存文件
        String fileName = "图纸清单_" + DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")
                .format(LocalDateTime.now()) + ".xlsx";
        String filePath = "/tmp/exports/" + fileName;

        try {
            // 确保目录存在
            Files.createDirectories(Paths.get("/tmp/exports"));

            // 写入文件
            try (FileOutputStream out = new FileOutputStream(filePath)) {
                workbook.write(out);
            }

            workbook.close();
            return fileName;

        } catch (IOException e) {
            throw new RuntimeException("导出文件失败: " + e.getMessage());
        }
    }

    /**
     * 格式化文件大小
     */
    private String formatFileSize(Long size) {
        if (size == null || size == 0)
            return "-";
        if (size < 1024)
            return size + "B";
        if (size < 1024 * 1024)
            return String.format("%.1fKB", size / 1024.0);
        return String.format("%.1fMB", size / (1024.0 * 1024.0));
    }

    /**
     * 批量上传图纸（异步）
     * 立即返回任务ID，后台异步处理
     *
     * @param projectId 项目ID
     * @param files     文件列表
     * @return 任务ID和初始状态
     */
    @Override
    public BatchUploadResultDTO batchUploadDrawingsAsync(Long projectId, List<String> files) {
        // 在异步执行前先获取当前登录用户信息（异步线程中无法获取）
        String currentTenantId = LoginHelper.getTenantId();
        String currentUsername = LoginHelper.getUsername();

        log.info("获取当前用户信息: tenantId={}, username={}", currentTenantId, currentUsername);

        // 生成任务ID
        String taskId = "BATCH_UPLOAD_" + System.currentTimeMillis() + "_" + projectId;

        // 初始化任务结果
        BatchUploadResultDTO result = new BatchUploadResultDTO();
        result.setTaskId(taskId);
        result.setStatus("PROCESSING");
        result.setProgress(0);
        result.setSuccess(true);
        result.setTotalFiles(files.size());
        result.setSuccessCount(0);
        result.setFailCount(0);
        result.setCreatedSitesCount(0);
        result.setCreatedDrawingsCount(0);
        result.setMessage("任务已创建，正在处理中...");

        // 存入缓存
        TASK_CACHE.put(taskId, result);

        log.info("创建异步上传任务: {}, 项目ID: {}, 文件数: {}", taskId, projectId, files.size());

        // 异步执行上传任务
        CompletableFuture.runAsync(() -> {
            try {
                // 调用内部方法，传入taskId和用户信息以支持进度更新
                BatchUploadResultDTO actualResult = batchUploadDrawingsInternal(
                        projectId, files, taskId, currentTenantId, currentUsername);

                // 更新结果
                actualResult.setTaskId(taskId);
                actualResult.setStatus("COMPLETED");
                actualResult.setProgress(100);
                TASK_CACHE.put(taskId, actualResult);

                log.info("异步上传任务完成: {}", taskId);
            } catch (Exception e) {
                log.error("异步上传任务失败: {}", taskId, e);

                // 更新为失败状态
                BatchUploadResultDTO errorResult = TASK_CACHE.get(taskId);
                if (errorResult != null) {
                    errorResult.setStatus("FAILED");
                    errorResult.setSuccess(false);
                    errorResult.setMessage("任务处理失败: " + e.getMessage());
                    TASK_CACHE.put(taskId, errorResult);
                }
            }
        });

        return result;
    }

    /**
     * 查询批量上传任务进度
     *
     * @param taskId 任务ID
     * @return 任务进度和结果
     */
    @Override
    public BatchUploadResultDTO getUploadTaskStatus(String taskId) {
        BatchUploadResultDTO result = TASK_CACHE.get(taskId);
        if (result == null) {
            // 任务不存在
            result = new BatchUploadResultDTO();
            result.setTaskId(taskId);
            result.setStatus("NOT_FOUND");
            result.setSuccess(false);
            result.setMessage("任务不存在或已过期");
        }
        return result;
    }

    /**
     * 批量上传图纸（同步，内部使用）
     * 根据文件名自动创建点位并上传图纸
     * 文件名格式: SL01A-0900-0100-0001 震盘料斗支架.SLDDRW
     *
     * @param projectId 项目ID
     * @param files     文件列表
     * @return 批量上传结果
     */
    @Override
    public BatchUploadResultDTO batchUploadDrawings(Long projectId, List<String> files) {
        // 同步调用时，直接从当前上下文获取用户信息
        String currentTenantId = LoginHelper.getTenantId();
        String currentUsername = LoginHelper.getUsername();
        return batchUploadDrawingsInternal(projectId, files, null, currentTenantId, currentUsername);
    }

    /**
     * 批量上传图纸内部方法（支持进度更新）
     */
    private BatchUploadResultDTO batchUploadDrawingsInternal(
            Long projectId, List<String> files, String taskId,
            String tenantId, String username) {
        BatchUploadResultDTO result = new BatchUploadResultDTO();
        result.setTaskId(taskId);
        result.setSuccess(true);
        result.setTotalFiles(files.size());
        result.setSuccessCount(0);
        result.setFailCount(0);
        result.setCreatedSitesCount(0);
        result.setCreatedDrawingsCount(0);
        result.setStatus("PROCESSING");
        result.setProgress(0);

        log.info("开始批量上传图纸, 项目ID: {}, 文件数量: {}", projectId, files.size());

        // 优化：批量查询所有OSS文件，避免循环中逐个查询
        Map<String, SysOssVo> ossMap = new HashMap<>();
        if (!files.isEmpty()) {
            List<SysOssVo> ossList = ossMapper.selectVoList(
                    new LambdaQueryWrapper<SysOss>().in(SysOss::getOssId, files));
            for (SysOssVo oss : ossList) {
                ossMap.put(String.valueOf(oss.getOssId()), oss);
            }
            log.info("批量查询OSS文件完成, 查询到 {} 个文件", ossList.size());
        }

        // 用于存储已创建的点位,避免重复创建
        Map<String, Long> siteCodeMap = new HashMap<>();

        // 先创建总设计图点位
        Long totalSiteId = createTotalDesignSite(projectId, tenantId, username);
        if (totalSiteId != null) {
            siteCodeMap.put("TOTAL_DESIGN", totalSiteId);
            result.setCreatedSitesCount(result.getCreatedSitesCount() + 1);
            result.getCreatedSites().add("总设计图");
        }

        // 分批处理，每批 20 个文件，避免一次性处理太多导致超时
        int batchSize = 20;
        int totalBatches = (int) Math.ceil((double) files.size() / batchSize);

        for (int batchIndex = 0; batchIndex < totalBatches; batchIndex++) {
            int fromIndex = batchIndex * batchSize;
            int toIndex = Math.min(fromIndex + batchSize, files.size());
            List<String> batchFiles = files.subList(fromIndex, toIndex);

            log.info("处理第 {}/{} 批文件, 范围: {}-{}",
                    batchIndex + 1, totalBatches, fromIndex + 1, toIndex);

            // 分批处理，每批使用独立事务
            processBatch(projectId, batchFiles, ossMap, siteCodeMap, result, tenantId, username);

            // 更新进度
            int progress = (int) ((batchIndex + 1) * 100.0 / totalBatches);
            result.setProgress(progress);

            // 如果有taskId，更新缓存中的进度
            if (taskId != null) {
                TASK_CACHE.put(taskId, result);
                log.info("任务 {} 进度: {}%", taskId, progress);
            }
        }

        // 生成结果消息
        result.setMessage(String.format(
                "批量上传完成: 总文件%d个, 成功%d个, 失败%d个, 创建点位%d个, 创建图纸%d个",
                result.getTotalFiles(),
                result.getSuccessCount(),
                result.getFailCount(),
                result.getCreatedSitesCount(),
                result.getCreatedDrawingsCount()));

        log.info("结果消息: {}", result.getMessage());
        return result;
    }

    /**
     * 分批处理文件上传（使用独立事务）
     */
    @Transactional(rollbackFor = Exception.class)
    public void processBatch(Long projectId, List<String> batchFiles,
            Map<String, SysOssVo> ossMap, Map<String, Long> siteCodeMap,
            BatchUploadResultDTO result, String tenantId, String username) {
        // 每个点位已存在图纸的缓存：siteId -> Set<drawName#drawType>
        Map<Long, Set<String>> existingDrawingsBySite = new HashMap<>();
        for (String file : batchFiles) {
            String fileName = null;
            try {
                // 从缓存Map中获取OSS文件信息，避免逐个查询
                SysOssVo sysOssVo = ossMap.get(file);

                if (sysOssVo == null) {
                    result.getErrors().add("文件ID: " + file + " - 文件不存在");
                    result.setFailCount(result.getFailCount() + 1);
                    continue;
                }

                fileName = sysOssVo.getOriginalName();

                if (StringUtils.isBlank(fileName)) {
                    result.getErrors().add("文件ID: " + file + " - 文件名为空");
                    result.setFailCount(result.getFailCount() + 1);
                    continue;
                }

                // 解析点位编码和图纸名称
                ParsedFileInfo fileInfo = parseFileName(fileName);
                if (fileInfo == null || StringUtils.isBlank(fileInfo.siteCode)) {
                    result.getErrors().add(fileName + ": 文件名格式不正确,无法解析点位编码");
                    result.setFailCount(result.getFailCount() + 1);
                    continue;
                }

                // 检查或创建点位（按编码分组）
                Long siteId = siteCodeMap.get(fileInfo.siteCode);
                if (siteId == null) {
                    // 创建或查找点位，使用汉字名称作为点位名称，编码作为唯一标识
                    SiteCreateResult siteResult = createSiteByCodeAndName(
                            projectId, fileInfo.siteCode, fileInfo.drawingName, tenantId, username);
                    if (siteResult != null && siteResult.siteId != null) {
                        siteId = siteResult.siteId;
                        siteCodeMap.put(fileInfo.siteCode, siteId);

                        // 只有新创建的点位才增加计数
                        if (siteResult.isNewCreated) {
                            result.setCreatedSitesCount(result.getCreatedSitesCount() + 1);
                            result.getCreatedSites().add(fileInfo.drawingName + "(" + fileInfo.siteCode + ")");
                        }
                    } else {
                        result.getErrors().add(fileName + ": 创建点位失败");
                        result.setFailCount(result.getFailCount() + 1);
                        continue;
                    }
                }
                // 如果该点位的已有图纸缓存未加载，批量加载
                Set<String> existingKeys = existingDrawingsBySite.get(siteId);
                if (existingKeys == null) {
                    existingKeys = baseMapper.selectList(
                            new LambdaQueryWrapper<DesignDrawing>()
                                    .eq(DesignDrawing::getProjectId, projectId)
                                    .eq(DesignDrawing::getSiteId, siteId))
                            .stream()
                            .map(d -> (d.getDrawName() == null ? "" : d.getDrawName()) + "#"
                                    + (d.getDrawType() == null ? "" : d.getDrawType()))
                            .collect(Collectors.toSet());
                    existingDrawingsBySite.put(siteId, existingKeys);
                }

                // 去重判断：同点位 + 同图纸名称 + 同后缀 视为重复
                String dedupeKey = fileInfo.drawingName + "#" + fileInfo.fileExtension;
                if (existingKeys.contains(dedupeKey)) {
                    result.getErrors().add(fileName + ": 已存在（按点位+图纸名+后缀），跳过");
                    // 不计入失败，不创建记录
                    continue;
                }
                DesignDrawing drawing = new DesignDrawing();
                drawing.setProjectId(projectId);
                drawing.setSiteId(siteId);
                drawing.setDrawName(fileInfo.drawingName);
                drawing.setDrawCode(extractAndCleanCode(fileInfo.drawingName));
                drawing.setDrawNo(fileInfo.siteCode);
                drawing.setDrawType(fileInfo.fileExtension);
                drawing.setVersion("1.0.0");
                drawing.setCreateBy(username); // 使用传入的用户名
                drawing.setTenantId(tenantId); // 使用传入的租户ID

                save(drawing);

                // 创建图纸文件关联
                DesignDrawingFiles drawingFile = new DesignDrawingFiles();
                drawingFile.setDrawId(drawing.getId());
                drawingFile.setAttachmentId(sysOssVo.getOssId());
                drawingFile.setVersion("1.0.0");
                drawingFile.setCreateBy(username); // 使用传入的用户名
                drawingFile.setTenantId(tenantId); // 使用传入的租户ID
                designDrawingFilesService.save(drawingFile);

                result.setSuccessCount(result.getSuccessCount() + 1);
                result.setCreatedDrawingsCount(result.getCreatedDrawingsCount() + 1);

            } catch (Exception e) {
                log.error("处理文件失败: {}", fileName, e);
                result.getErrors().add((fileName != null ? fileName : file) + ": " + e.getMessage());
                result.setFailCount(result.getFailCount() + 1);
                result.setSuccess(false);
            }
        }
    }

    /**
     * 创建总设计图点位
     */
    private Long createTotalDesignSite(Long projectId, String tenantId, String username) {
        try {
            // 检查是否已存在总设计图点位
            LambdaQueryWrapper<DesignDrawingSite> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(DesignDrawingSite::getProjectId, projectId)
                    .eq(DesignDrawingSite::getSiteName, "总设计图")
                    .eq(DesignDrawingSite::getParentId, 0L);

            DesignDrawingSite existSite = designDrawingSiteService.getOne(wrapper);
            if (existSite != null) {
                return existSite.getId();
            }

            // 创建总设计图点位
            DesignDrawingSite site = new DesignDrawingSite();
            site.setSiteName("总设计图");
            site.setParentId(0L);
            site.setProjectId(projectId);
            site.setRemark("自动创建的总设计图点位");
            site.setTenantId(tenantId); // 使用传入的租户ID
            site.setCreateBy(username); // 使用传入的用户名
            designDrawingSiteService.save(site);
            return site.getId();
        } catch (Exception e) {
            log.error("创建总设计图点位失败", e);
            return null;
        }
    }

    /**
     * 根据点位编码和名称创建点位
     *
     * @param projectId 项目ID
     * @param siteCode  点位编码（如：SL01A-0900-0100-0001）
     * @param siteName  点位名称（如：震盘料斗支架）
     * @param tenantId  租户ID
     * @param username  用户名
     * @return 点位ID和是否新创建的标识
     */
    private SiteCreateResult createSiteByCodeAndName(Long projectId, String siteCode, String siteName,
            String tenantId, String username) {
        try {
            // 检查点位是否已存在（按编码查询）
            LambdaQueryWrapper<DesignDrawingSite> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(DesignDrawingSite::getProjectId, projectId)
                    .eq(DesignDrawingSite::getSiteCode, siteCode);

            DesignDrawingSite existSite = designDrawingSiteService.getOne(wrapper);
            if (existSite != null) {
                log.info("点位已存在，直接使用: {} - {}", siteCode, existSite.getSiteName());
                return new SiteCreateResult(existSite.getId(), false); // 返回已存在的点位ID，标记为未新建
            }

            // 解析父点位编码并创建父点位（如果需要）
            Long parentId = findOrCreateParentSite(projectId, siteCode, siteName, tenantId, username);

            // 创建新点位
            DesignDrawingSite site = new DesignDrawingSite();
            site.setSiteCode(siteCode); // 点位编码
            site.setSiteName(siteName); // 点位名称（汉字）
            site.setParentId(parentId != null ? parentId : 0L);
            site.setProjectId(projectId);
            site.setRemark("批量上传自动创建");
            site.setTenantId(tenantId); // 使用传入的租户ID
            site.setCreateBy(username); // 使用传入的用户名
            designDrawingSiteService.save(site);

            log.info("新创建点位: {} - {}", siteCode, siteName);
            return new SiteCreateResult(site.getId(), true); // 返回新建的点位ID，标记为新建
        } catch (Exception e) {
            log.error("创建点位失败: " + siteCode + " - " + siteName, e);
            return null;
        }
    }

    /**
     * 解析文件名
     * 文件名格式: SL01A-0900-0100-0001 震盘料斗支架.SLDDRW
     *
     * @param fileName 文件名
     * @return 解析结果
     */
    private ParsedFileInfo parseFileName(String fileName) {
        try {
            // 去掉扩展名
            int lastDotIndex = fileName.lastIndexOf('.');
            String nameWithoutExt = lastDotIndex > 0 ? fileName.substring(0, lastDotIndex) : fileName;
            String extension = lastDotIndex > 0 ? fileName.substring(lastDotIndex + 1) : "";

            // 解析点位编码和图纸名称
            // 格式: SL01A-0900-0100-0001 震盘料斗支架
            String[] parts = nameWithoutExt.split(" ", 2);

            if (parts.length == 0) {
                return null;
            }

            ParsedFileInfo info = new ParsedFileInfo();
            info.siteCode = parts[0].trim();
            info.drawingName = parts.length > 1 ? parts[1].trim() : parts[0].trim();
            info.fileExtension = extension;
            info.originalFileName = fileName;

            return info;
        } catch (Exception e) {
            log.error("解析文件名失败: " + fileName, e);
            return null;
        }
    }

    /**
     * 查找或创建父点位
     * 编码格式: SL01A-0900-0200-0001
     * 父点位编码: SL01A-0900-0200-0000
     *
     * @param projectId 项目ID
     * @param siteCode  当前点位编码
     * @param siteName  当前点位名称
     * @param tenantId  租户ID
     * @param username  用户名
     * @return 父点位ID，如果没有父点位则返回null
     */
    private Long findOrCreateParentSite(Long projectId, String siteCode, String siteName,
            String tenantId, String username) {
        try {
            // 解析编码部分
            String[] parts = siteCode.split("-");
            if (parts.length != 4) {
                // 编码格式不正确，返回null（作为根节点）
                return null;
            }

            String lastPart = parts[3];

            // 如果最后一段已经是0000，说明这是一个组的根节点，不需要父节点
            if ("0000".equals(lastPart)) {
                return null;
            }

            // 构造父点位编码：将最后一段改为0000
            String parentCode = parts[0] + "-" + parts[1] + "-" + parts[2] + "-0000";

            // 查找父点位
            LambdaQueryWrapper<DesignDrawingSite> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(DesignDrawingSite::getProjectId, projectId)
                    .eq(DesignDrawingSite::getSiteCode, parentCode);

            DesignDrawingSite parentSite = designDrawingSiteService.getOne(wrapper);

            if (parentSite != null) {
                return parentSite.getId();
            }

            // 父点位不存在，自动创建
            // 父点位名称：使用当前点位名称的主体部分
            String parentName = extractParentName(siteName);

            DesignDrawingSite newParent = new DesignDrawingSite();
            newParent.setSiteCode(parentCode);
            newParent.setSiteName(parentName);
            newParent.setParentId(0L); // 父点位作为根节点
            newParent.setProjectId(projectId);
            newParent.setRemark("批量上传自动创建（父节点）");
            newParent.setTenantId(tenantId); // 设置租户ID
            newParent.setCreateBy(username); // 设置创建人
            designDrawingSiteService.save(newParent);

            log.info("自动创建父点位: " + parentCode + " - " + parentName);
            return newParent.getId();

        } catch (Exception e) {
            log.error("查找或创建父点位失败: " + siteCode, e);
            return null;
        }
    }

    /**
     * 提取父点位名称
     * 例如: "震盘料斗支架" -> "震盘料斗支架组"
     */
    private String extractParentName(String siteName) {
        if (StringUtils.isBlank(siteName)) {
            return "未命名组";
        }
        // 简单处理：如果名称不包含"组"，则添加"组"后缀
        if (!siteName.contains("组")) {
            return siteName + "组";
        }
        return siteName;
    }

    /**
     * 文件名解析结果内部类
     */
    private static class ParsedFileInfo {
        String siteCode; // 点位编码
        String drawingName; // 图纸名称
        String fileExtension; // 文件扩展名
        String originalFileName; // 原始文件名
    }

    /**
     * 点位创建结果内部类
     */
    private static class SiteCreateResult {
        Long siteId; // 点位ID
        boolean isNewCreated; // 是否新创建

        SiteCreateResult(Long siteId, boolean isNewCreated) {
            this.siteId = siteId;
            this.isNewCreated = isNewCreated;
        }
    }

    /**
     * 从文件名中提取编码并去除横杠。
     * 编码规则：以非横杠、非空格字符开头，后跟零个或多个（横杠 + 非横杠、非空格字符）组合。
     *
     * @param fullFileName 完整的文件名，例如："SL01A-0900-0200-0001 震盘料斗2.PDF"
     * @return 提取并去除横杠后的编码，例如："SL01A090002000001"；如果未匹配到，则返回空字符串。
     */
    public static String extractAndCleanCode(String fullFileName) {
        if (fullFileName == null || fullFileName.trim().isEmpty()) {
            return "";
        }

        // 正则表达式：匹配编码部分
        // ^ - 匹配字符串开头
        // [^-\\s]+ - 匹配一个或多个非横杠、非空格的字符（编码的第一部分）
        // (-[^-\\s]+)* - 匹配零个或多个（横杠 + 非横杠、非空格字符）的组合（编码的后续部分）
        Pattern pattern = Pattern.compile("^[^-\\s]+(-[^-\\s]+)*");
        Matcher matcher = pattern.matcher(fullFileName);

        if (matcher.find()) {
            // 提取匹配到的编码（例如 "SL01A-0900-0200-0001"）
            String codeWithDashes = matcher.group();
            // 去除所有横杠并返回
            return codeWithDashes.replace("-", "");
        }

        // 如果没有找到匹配的编码格式，返回空字符串
        return "";
    }
}
